Skip to content

Comments

Generate Foulborn uniques programmatically from ModFoulbornMap#9478

Open
EtherealCarnivore wants to merge 6 commits intoPathOfBuildingCommunity:devfrom
EtherealCarnivore:foulborn-generated-uniques
Open

Generate Foulborn uniques programmatically from ModFoulbornMap#9478
EtherealCarnivore wants to merge 6 commits intoPathOfBuildingCommunity:devfrom
EtherealCarnivore:foulborn-generated-uniques

Conversation

@EtherealCarnivore
Copy link

@EtherealCarnivore EtherealCarnivore commented Feb 21, 2026

Summary

Alternative to #9432. Instead of duplicating every unique as static files (+5768 lines), this generates all ~245 Foulborn items programmatically using the existing ModFoulbornMap.lua data and the established pattern in Generated.lua (same approach as Paradoxica, Watcher's Eye, Forbidden Flame/Flesh, etc.).

  • Each Foulborn unique becomes a separate generated item (e.g., "Foulborn Alpha's Howl")
  • Variants represent different possible Foulborn mutation mods — user picks which mutation via the variant dropdown
  • Base mods are copied from the original unique (using "Current" variant for multi-variant items)
  • Skin of the Lords uses alt variant to allow selecting both keystone and Foulborn mutation
  • Existing item.foulborn detection and threshold counting work without changes

Changes

  • src/Modules/Data.lua — Load foulbornMap before Generated.lua runs (1 line)
  • src/Data/Uniques/Special/Generated.lua — Foulborn generation loop + buildFoulbornSkinOfTheLords() for the tree-dependent case
  • spec/System/TestFoulborn_spec.lua — 9 tests covering generation, parsing, variants, implicits, alt variants

Total: ~370 lines added (vs ~5768 in #9432)

Test plan

  • Docker test suite: 204/204 passing (0 failures)
  • 9 Foulborn-specific tests covering:
    • Item count (245 generated)
    • Raw text structure (base type, variants, requires, implicits)
    • Item parser integration (foulborn flag, baseName, title, variantList)
    • Multi-variant originals (The Formless Flame → Current variant base)
    • Implicit handling (Call of the Brotherhood)
    • No empty base types across all generated items
    • Skin of the Lords alt variant (keystone + mutation dropdowns)
  • Manual GUI testing in PoB (equip items, switch variants, threshold mechanics)

Instead of duplicating every unique as separate entries in a new
Data/Uniques/Foulborn/ directory (+5768 lines), generate Foulborn
items in Generated.lua using the existing ModFoulbornMap data.

Each Foulborn unique becomes a separate generated item where variants
represent different possible Foulborn mutations. This follows the
same pattern used for Paradoxica, Watcher's Eye, and other generated
uniques.

- Load data.foulbornMap in Data.lua before Generated.lua runs
- Parse each original unique's current variant (base, requires,
  implicits, mods) and combine with Foulborn mutation mods
- Generates 244 Foulborn items from the existing map data
- Existing foulborn detection (Item.lua) and counting (CalcSetup.lua)
  work without changes
Skin of the Lords is a tree-dependent generated unique, so it's built
separately in buildFoulbornSkinOfTheLords() called from
buildTreeDependentUniques(). Uses alt variant to give users two
dropdowns: one for keystone selection, one for Foulborn mutation.
@EtherealCarnivore
Copy link
Author

Manual GUI Testing Complete

Tested in PoB on Windows with ~20 items across different categories plus Skin of the Lords:

  • Item browser: Foulborn items appear correctly when searching "Foulborn"
  • Variant dropdown: Switching between Foulborn mutation variants updates the tooltip and calcs
  • Base types: Correct base types for helmets, rings, body armour, etc.
  • Implicits: Ring implicits display correctly (e.g., Foulborn Call of the Brotherhood)
  • Skin of the Lords: Both dropdowns work — keystone selection + Foulborn mutation selection via alt variant
  • Threshold mechanics: Equipping 4+ Foulborn uniques activates threshold bonuses as expected
  • No regressions: Non-Foulborn uniques unaffected

Combined with the automated test suite (204/204 passing, 9 Foulborn-specific tests validating all 245 generated items), this covers both structural correctness and runtime behavior.

@Nightblade Nightblade added the enhancement New feature, calculation, or mod label Feb 21, 2026
Add cached mod parse results for all Foulborn mutation mods that
appear on generated Foulborn unique items.
Export script that dumps ModEquivalencies.dat rows to check if
MutatedUnique mod pairings are stored there. If they are, we can
automate Foulborn original↔mutation mod pairing instead of
maintaining a manual table.
Instead of appending mutation mods as variants to the original's base
mods, the new logic properly replaces specific original mods with their
Foulborn mutations -- matching the actual in-game behavior.

Key changes:
- Add ModFoulbornPairs.lua: mutation slot pairing table (239 uniques)
  extracted from PR PathOfBuildingCommunity#9432's static item data, defining which original
  mods get removed and what replaces them per mutation slot
- Rewrite Generated.lua Foulborn block: for each unique, enumerate all
  non-empty subsets of mutation slots (2^n - 1 items per unique), remove
  matched mods and add mutated replacements with "(mutated)" suffix
- Add warning system: ConPrintf flags uniques in ModFoulbornMap that
  lack pairing data in ModFoulbornPairs (22 uniques currently)
- Update tests: 14 tests covering item count (437), per-unique counts,
  mod replacement verification, implicit handling, Skin of the Lords
- Remove dumpModEquivalencies.lua (no longer needed)

Total: 437 generated Foulborn items with correct replacement semantics.
@EtherealCarnivore
Copy link
Author

Hey @LocalIdentity -- big update here worth calling out.

So after looking more closely at how the actual in-game Foulborn mechanic works (and cross-referencing against the static data in #9432), I realized the initial approach of just appending mutation mods as extra variants was wrong. What actually happens is specific mods on the original unique get replaced by the mutation -- it's not additive, it's a swap.

Took a bit of digging but I ended up extracting a pairing table (ModFoulbornPairs.lua) from the diff between the static items in #9432 and the originals -- basically mapping out which original mod gets removed and what the mutated replacement is, per slot. That gave us 239 uniques with clean pairings.

The generation logic now works like this: for each unique, we look at how many mutation slots it has, enumerate all the possible slot combinations (so if an item has 2 mutation slots you get 3 variants -- slot A, slot B, both), remove the matched original mods, and splice in the mutated versions with the (mutated) suffix. That bumps us from 245 to 437 generated items, which tracks with the combinatorics.

There's 22 uniques from the map that don't have pairing data yet -- the code flags those with a warning at load time so they're easy to spot and fill in later. Everything else should be matching the actual replacement behavior now.

Tests went from 9 to 14, covering the new counts, per-unique variant counts, and verifying that specific mods actually get replaced (not just appended). Full suite still green.

The static data from #9432 was honestly super clutch for figuring out the pairing logic -- wouldn't have been able to extract those mod equivalencies without it.

@EtherealCarnivore
Copy link
Author

Regenerated ModCache.lua — the cache was stale after the mod replacement rewrite in the last commit. The check_modcache CI job should pass now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature, calculation, or mod

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants